/***********************************************************************************
**  
* @copyright (c) 2010-2019, ChengDu Duyer Technology Co., LTD. All Right Reserved.
*
************************************************************************************/
/**
* @file	    mjs_mysql.cpp
* @version     
* @brief      
* @author   duye
* @date	    2016-12-27
* @note 
*
* 1. 2016-12-27 duye Created this file
*/
#include <duye_logger.h>
#include <duye_helper.h>

#include <mjs_mysql_result.h>
#include <mjs_mysql.h>

static const int8* DUYE_LOG_PREFIX = "mangjings.mysql";

MJSMysql::MJSMysql() : m_conn(NULL) {}
MJSMysql::~MJSMysql() {
	if (m_conn) {
		mysql_close(m_conn);
		m_conn = NULL;
	}
}

MJSMysql& MJSMysql::ins() {
	static MJSMysql ins;
	return ins;
}

bool MJSMysql::init(const std::string& addr, const uint16 port, const std::string& user, const std::string& pwd, const std::string& database) {
	if (m_conn) {
		DUYE_WARN("mysql had init");
		return true;
	}

	m_conn = mysql_init(NULL);
	if (!m_conn) {
		DUYE_ERROR("mysql_init() Error %u: %s\n", mysql_errno(m_conn), mysql_error(m_conn));
	 	return false;
	}

	if (mysql_real_connect(m_conn, addr.c_str(), user.c_str(), pwd.c_str(), database.c_str(), port, NULL, 0) == NULL) {
	  	DUYE_ERROR("mysql_real_connect() Error %u: %s\n", mysql_errno(m_conn), mysql_error(m_conn));
	  	return false;
	}

	DUYE_INFO("connect mysql database [OK]");

	return true;
}

int32 MJSMysql::execute(const std::string& sql) {
	if (!valid()) {
		return false;
	}

	MJSMysqlResult result;
	if (!commit(sql, result)) {
		return -1;
	}

	return result.affected();
}

int32 MJSMysql::execute(const std::string& sql, const MJSMysql::Params& params) {
	if (!valid()) {
		return false;
	}

	std::string real_sql;
	if (params.empty()) {
		return execute(sql);
	} else {
		joinsql(sql, params, real_sql);
		return execute(real_sql);
	}
}

bool MJSMysql::query(const std::string& sql, MJSMysqlResult& result) {
	if (!valid()) {
		return false;
	}

	return commit(sql, result);
}

bool MJSMysql::query(const std::string& sql, const MJSMysql::Params& params, MJSMysqlResult& result) {
	if (!valid()) {
		return false;
	}

	std::string real_sql;
	if (params.empty()) {
		return query(sql, result);
	} else {
		DUYE_TRACE("params.size() = %d", params.size());
		joinsql(sql, params, real_sql);
		return query(real_sql, result);
	}
}

int32 MJSMysql::update(const std::string& sql) {
	if (!valid()) {
		return -1;
	}

	MJSMysqlResult result;
	if (!commit(sql, result)) {
		return -1;
	}

	return result.affected();
}

int32 MJSMysql::update(const std::string& sql, const MJSMysql::Params& params) {
	if (!valid()) {
		return false;
	}

	std::string real_sql;
	if (params.empty()) {
		update(sql);
	} else {
		joinsql(sql, params, real_sql);
		return update(real_sql);
	}

	return -1;
}

bool MJSMysql::valid() {
	if (!m_conn) {
		DUYE_ERROR("mysql don't connet, call init() please");
		return false;
	}

	return true;
}

bool MJSMysql::joinsql(const std::string& sql, const MJSMysql::Params& params, std::string& out_sql) {
	std::vector<std::string> split_array;
	duye::StrHelper::split(sql, '?', split_array);

	if (split_array.size() != params.size() && split_array.size() != params.size() + 1) {
		DUYE_ERROR("sql params number unmatch, in_params_num=%d, split_num=%d", params.size(), split_array.size());
		return false;
	}

	uint32 i = 0;
	for (; i < params.size(); i++) {
		DUYE_TRACE("%s", split_array[i].c_str());
		out_sql.append(split_array[i] + "'" + params[i] + "'");
	}

	if (split_array.size() == params.size() + 1) {
		out_sql.append(split_array[i]);
	}

	return true;
}

bool MJSMysql::commit(const std::string& sql, MJSMysqlResult& result) {
	if (sql.empty()) {
		DUYE_WARN("sql is empty");
		return false;
	}

	DUYE_DEBUG("sql = '%s'", sql.c_str());

	if (mysql_query(m_conn, sql.c_str()) < 0) {
		DUYE_ERROR("%s", mysql_error(m_conn));
		return false;
	}

	result.setAffected(mysql_affected_rows(m_conn));

	MYSQL_RES* sql_result = mysql_store_result(m_conn);
	if (!sql_result) {
		return true;
	}

	uint32 row_num = mysql_num_rows(sql_result);

	DUYE_DEBUG("query get rows=%d", row_num);

	if (row_num > 0) {
  	  	// <id, col_name>
  	  	std::map<uint32, std::string> col_names_map;
  	  	uint32 index = 0;
  	  	MYSQL_FIELD* field = NULL;
        while ((field = mysql_fetch_field(sql_result))) {
 			col_names_map[index++] = field->name; 			
     	}

     	DUYE_TRACE("column name list >>>");
     	for (uint32 i = 0; i < col_names_map.size(); i++) {
     		DUYE_TRACE("%s ", col_names_map[i].c_str());
     	}

     	int32 fields_num = mysql_num_fields(sql_result);
     	DUYE_TRACE("fields_num = %d", fields_num);

     	if ((uint32)fields_num != col_names_map.size()) {
     		DUYE_ERROR("column numbers unmatch, fields_num=%d != col_names_map.size()=%d", fields_num, col_names_map.size());
     	} else {
     		uint32 row_index = 0;
			MYSQL_ROW row = mysql_fetch_row(sql_result);
			if (!row) {
				DUYE_ERROR("row == NULL");
			}

			do {
				row_index++;
				MJSMysqlRow row_result;
				for (int32 i = 0; i < fields_num; i++) {
					if (row[i]) {
						row_result.addPara(col_names_map[i], row[i]);
						DUYE_TRACE("%d. col=%s value=%s", row_index, col_names_map[i].c_str(), row[i]);				
					}
			  	}

			  	result.insertRow(row_result);
			} while ((row = mysql_fetch_row(sql_result)));
		}
	}

	mysql_free_result(sql_result);

	return true;
}